using System.Runtime.InteropServices;

namespace Pinwheel.Kernel
{
    public static class SystemInfo
    {
        public static int GetPhysicalCoreCount()
        {
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                return GetPhysicalCoreCountWindows();
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            {
                return GetPhysicalCoreCountLinux();
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
            {
                return GetPhysicalCoreCountMacOS();
            }
            else
            {
                throw new PlatformNotSupportedException("Unknown platform");
            }
        }

        #region Windows

        [StructLayout(LayoutKind.Sequential)]
        struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX
        {
            public int Relationship;
            public int Size;
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool GetLogicalProcessorInformationEx(
            int relationshipType,
            IntPtr buffer,
            ref int returnLength);

        const int RelationProcessorCore = 0;

        static int GetPhysicalCoreCountWindows()
        {
            int returnLength = 0;
            GetLogicalProcessorInformationEx(RelationProcessorCore, IntPtr.Zero, ref returnLength);

            IntPtr buffer = Marshal.AllocHGlobal(returnLength);
            try
            {
                if (GetLogicalProcessorInformationEx(RelationProcessorCore, buffer, ref returnLength))
                {
                    int count = 0;
                    int offset = 0;

                    while (offset < returnLength)
                    {
                        var info = Marshal.PtrToStructure<SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX>(
                            IntPtr.Add(buffer, offset));
                        if (info.Relationship == RelationProcessorCore)
                            count++;
                        offset += info.Size;
                    }
                    return count;
                }
                return -1;
            }
            finally
            {
                Marshal.FreeHGlobal(buffer);
            }
        } 

        #endregion
        
        #region Linux
        
        [DllImport("libc", EntryPoint = "sysconf")]
        private static extern long Sysconf(int name);

        private const int _SC_NPROCESSORS_ONLN = 84; // Logical processors
        private const int _SC_NPROCESSORS_CONF = 83; // Configured processors

        static int GetPhysicalCoreCountLinux()
        {
            long count = Sysconf(_SC_NPROCESSORS_ONLN);
            return count > 0 ? (int)count : -1;
        }
        
        #endregion
        
        
        #region MacOS
        
        [DllImport("libc")]
        private static extern int sysctlbyname(
            [MarshalAs(UnmanagedType.LPStr)] string name,
            IntPtr oldp,
            ref IntPtr oldlenp,
            IntPtr newp,
            IntPtr newlen);
        
        static int GetPhysicalCoreCountMacOS()
        {
            const string key = "hw.physicalcpu";

            // 用于保存结果长度的变量
            IntPtr size = (IntPtr)sizeof(int);
            // 用于保存结果的变量
            IntPtr result = Marshal.AllocHGlobal(sizeof(int));

            try
            {
                // 调用 sysctlbyname 并写入结果到 result
                if (sysctlbyname(key, result, ref size, IntPtr.Zero, IntPtr.Zero) == 0)
                {
                    // 读取结果
                    return Marshal.ReadInt32(result);
                }
                return -1;
            }
            finally
            {
                // 释放仅分配的 result
                Marshal.FreeHGlobal(result);
            }
        }
        
        #endregion
    }
}